﻿namespace MicroCloud.Hangfire
{
    /// <summary>
    /// 后台任务模块(Hangfire)基类
    /// </summary>
    [Description("后台任务模块(Hangfire)")]
    [DependsOnPacks(typeof(AspNetCorePack))]
    public abstract class HangfirePackBase : AspPackBase
    {
        #region "属性"
        /// <summary>
        /// 获取 模块级别
        /// <para>级别越小越先启动</para>
        /// </summary>
        public override PackLevel Level => PackLevel.Application;

        /// <summary>
        /// 获取 模块启动顺序编号
        /// <para>模块启动的顺序先按级别启动，级别内部再按此顺序启动，级别默认为0，表示无依赖，需要在同级别有依赖顺序的时候，再重写为 >0 的顺序值。</para>
        /// </summary>
        public override int OrderNo => 0;

        #endregion

        #region "方法"
        #region "将模块服务添加到依赖注入服务容器中"
        /// <summary>
        /// 将模块服务添加到依赖注入服务容器中
        /// </summary>
        /// <param name="services">依赖注入服务容器</param>
        /// <returns></returns>
        public override IServiceCollection AddServices(IServiceCollection services)
        {
            IConfiguration configuration = services.GetConfiguration();
            bool enabled = configuration["MicroCloud:Hangfire:Enabled"].CastTo(false);
            if (!enabled)
            {
                return services;
            }

            Action<IGlobalConfiguration> hangfireAction = GetHangfireAction(services);
            services.AddHangfire(hangfireAction);
            var optionsAction = GetBackgroundJobServerOptionsAction(configuration);
            services.AddHangfireServer(optionsAction);

            return services;
        }
        #endregion
        #region "应用AspNetCore的服务业务"
        /// <summary>
        /// 应用AspNetCore的服务业务
        /// </summary>
        /// <param name="app">Asp应用程序构建器</param>
        public override void UsePack(WebApplication app)
        {
            IServiceProvider serviceProvider = app.Services;

            IConfiguration configuration = serviceProvider.GetRequiredService<IConfiguration>();
            bool enabled = configuration["MicroCloud:Hangfire:Enabled"].CastTo(false);
            if (!enabled)
            {
                return;
            }

            IGlobalConfiguration globalConfiguration = serviceProvider.GetService<IGlobalConfiguration>();
            globalConfiguration.UseLogProvider(new AspNetCoreLogProvider(serviceProvider.GetService<ILoggerFactory>()));

            string url = configuration["MicroCloud:Hangfire:DashboardUrl"].CastTo("/hangfire");
            DashboardOptions dashboardOptions = GetDashboardOptions(configuration);
            app.UseHangfireDashboard(url, dashboardOptions);

            //Hangfire作业运行器服务数组
            //IHangfireJobRunner jobRunner = serviceProvider.GetService<IHangfireJobRunner>();
            //jobRunner?.Start();
            IEnumerable<IHangfireJobRunner> jobRunners = serviceProvider.GetServices<IHangfireJobRunner>();
            if (jobRunners != null)
            {
                foreach (var jobRunner in jobRunners)
                {
                    jobRunner?.Start();
                }
            }

            IsEnabled = true;
        }
        #endregion

        #region "AddHangfire委托，重写可配置Hangfire服务，比如使用UseSqlServerStorage等"
        /// <summary>
        /// AddHangfire委托，重写可配置Hangfire服务，比如使用UseSqlServerStorage等
        /// </summary>
        /// <param name="services">服务容器</param>
        /// <returns></returns>
        protected virtual Action<IGlobalConfiguration> GetHangfireAction(IServiceCollection services)
        {
            IConfiguration configuration = services.GetConfiguration();

            string storageConnectionString = configuration["MicroCloud:Hangfire:StorageConnectionString"].CastTo("");
            var storageType = configuration["MicroCloud:Hangfire:StorageType"].CastTo("Memory").Trim().ToUpper();
            switch (storageType)
            {
                case "SQLSERVER":
                    return config => config.UseSqlServerStorage(storageConnectionString);
                case "POSTGRESQL":
                case "KDBNDP":
                    return config => config.UsePostgreSqlStorage(configure =>
                    {
                        configure.UseNpgsqlConnection(storageConnectionString);
                    });
                case "SQLITE":
                    return config => config.UseSQLiteStorage(storageConnectionString);
                case "MYSQL":
                    return config => config.UseStorage(new MySqlStorage(storageConnectionString, new MySqlStorageOptions
                    {
                        //CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                        //SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                        //QueuePollInterval = TimeSpan.Zero,
                        //UseRecommendedIsolationLevel = true,
                        //DisableGlobalLocks = true
                    }));
                case "ORACLE":
                    return config => config.UseStorage(new OracleStorage(storageConnectionString));
                case "MONGO":
                    //var mongoUrlBuilder = new MongoUrlBuilder(storageConnectionString);
                    //var mongoClient = new MongoClient(mongoUrlBuilder.ToMongoUrl());
                    //return config => config.UseMongoStorage(mongoClient, mongoUrlBuilder.DatabaseName, new MongoStorageOptions
                    //{
                    //    MigrationOptions = new MongoMigrationOptions
                    //    {
                    //        MigrationStrategy = new MigrateMongoMigrationStrategy(),
                    //        BackupStrategy = new CollectionMongoBackupStrategy()
                    //    },
                    //    Prefix = "MicroCloud.hangfire",
                    //    CheckConnection = true
                    //});
                    return config => config.UseMongoStorage(storageConnectionString, new MongoStorageOptions
                    {
                        MigrationOptions = new MongoMigrationOptions
                        {
                            MigrationStrategy = new MigrateMongoMigrationStrategy(),
                            BackupStrategy = new CollectionMongoBackupStrategy()
                        },
                        Prefix = "MicroCloud.hangfire",
                        CheckConnection = true
                    });
                case "REDIS":
                    return config => config.UseRedisStorage(storageConnectionString);
                case "MEMORY":
                    return config => config.UseMemoryStorage();
                default:
                    break;
            }
            return config => config.UseMemoryStorage();
        }
        #endregion
        #region "获取后台作业服务器选项"
        /// <summary>
        /// 获取后台作业服务器选项
        /// </summary>
        /// <param name="configuration">系统配置信息</param>
        /// <returns></returns>
        protected virtual Action<IServiceProvider, BackgroundJobServerOptions> GetBackgroundJobServerOptionsAction(IConfiguration configuration)
        {
            int workerCount = configuration["MicroCloud:Hangfire:WorkerCount"].CastTo(0);
            string serverName = configuration["MicroCloud:Hangfire:ServerName"].CastTo("");
            return (provider, options) =>
            {
                if (workerCount > 0)
                {
                    options.WorkerCount = workerCount;
                }
                else
                {
                    //默认并发线程是cpu的5倍
                    options.WorkerCount = Environment.ProcessorCount * 5;
                }
                if (!serverName.IsMissing())
                {
                    options.ServerName = serverName;
                }
            };
        }
        #endregion
        #region "获取Hangfire仪表盘选项"
        /// <summary>
        /// 获取Hangfire仪表盘选项
        /// </summary>
        /// <param name="configuration">系统配置信息</param>
        /// <returns></returns>
        protected virtual DashboardOptions GetDashboardOptions(IConfiguration configuration)
        {
            DashboardOptions dashboardOptions = new();
            string authorizationType = configuration["MicroCloud:Hangfire:AuthorizationType"].CastTo("").ToLower().Trim();
            authorizationType = authorizationType.IsMissing() ? "anonymous" : authorizationType;
            switch (authorizationType)
            {
                //匿名
                case "anonymous":
                    dashboardOptions.Authorization = new[] { new AnonymousAuthorizationFilter() };
                    break;
                //基本身份验证
                case "basic":
                    var basicOptions = GetBasicOptions(configuration);
                    dashboardOptions.Authorization = new[] { new BasicAuthAuthorizationFilter(basicOptions) };
                    break;

            }
            //string[] roles = configuration["MicroCloud:Hangfire:Roles"].CastTo("").Split(",", true);
            ////限制角色存在时，才启用角色限制
            //if (roles.Length > 0)
            //{
            //    dashboardOptions.Authorization = new[] { new RoleDashboardAuthorizationFilter(roles) };
            //}

            //

            return dashboardOptions;
        }
        #endregion

        #endregion

        #region "获取Hangfire基本身份验证的选项"
        /// <summary>
        /// 获取Hangfire基本身份验证的选项
        /// </summary>
        /// <param name="configuration">系统配置信息</param>
        /// <returns></returns>
        protected virtual BasicAuthAuthorizationFilterOptions GetBasicOptions(IConfiguration configuration)
        {
            IConfigurationSection section = configuration.GetSection("MicroCloud:Hangfire:BasicOptions");
            if (!section.Exists())
            {
                throw new Exception(I18N.T("Hangfire基本身份验证的选项不存在，请在 appsetting.json 配置 MicroCloud:Hangfire:BasicOptions 节点"));
            }
            var basicOptions = section.Get<BasicAuthAuthorizationFilterOptions>();
            if (basicOptions != null)
            {
                //var sslRedirect = configuration["MicroCloud:Hangfire:BasicOptions:SslRedirect"].CastTo(false);
                //var requireSsl = configuration["MicroCloud:Hangfire:BasicOptions:RequireSsl"].CastTo(false);
                //var loginCaseSensitive = configuration["MicroCloud:Hangfire:BasicOptions:LoginCaseSensitive"].CastTo(false);

                section = configuration.GetSection("MicroCloud:Hangfire:BasicOptions:Users");
                var basicOptionsUsers = section.Get<IEnumerable<BasicAuthAuthorizationUser>>();

                basicOptions.Users = basicOptionsUsers;
            }

            return basicOptions ?? new BasicAuthAuthorizationFilterOptions();
        }
        #endregion

    }
}
